1 /**
2    Helper functions for dealing with UDAs, written before hasUDA and
3    others were added to Phobos.
4  */
5 module unit_threaded.uda;
6 
7 private template Identity(T...) if (T.length > 0) {
8     static if (__traits(compiles, { alias x = T[0]; }))
9         alias Identity = T[0];
10     else
11         enum Identity = T[0];
12 }
13 
14 /**
15  * For the given module, return true if this module's member has
16  * the given UDA. UDAs can be types or values.
17  */
18 template HasAttribute(alias module_, string moduleMember, alias attribute) {
19     import unit_threaded.meta : importMember;
20     import std.meta : Filter;
21 
22     alias member = Identity!(__traits(getMember, module_, moduleMember));
23 
24     static if (!__traits(compiles, __traits(getAttributes, member)))
25         enum HasAttribute = false;
26     else {
27         enum isAttribute(alias T) = is(TypeOf!T == attribute);
28         alias attrs = Filter!(isAttribute, __traits(getAttributes, member));
29 
30         static assert(attrs.length == 0 || attrs.length == 1,
31                 text("Maximum number of attributes is 1 for ", attribute));
32 
33         static if (attrs.length == 0) {
34             enum HasAttribute = false;
35         } else {
36             enum HasAttribute = true;
37         }
38     }
39 }
40 
41 /**
42  * For the given module, return true if this module's member has
43  * the given UDA. UDAs can be types or values.
44  */
45 template GetAttributes(alias module_, string member, A) {
46     import unit_threaded.meta : importMember;
47     import std.meta : Filter;
48 
49     mixin(importMember!module_(member));
50     enum isAttribute(alias T) = is(TypeOf!T == A);
51     alias GetAttributes = Filter!(isAttribute, __traits(getAttributes, mixin(member)));
52 }
53 
54 /**
55  * Utility to allow checking UDAs regardless of whether the template
56  * parameter is or has a type
57  */
58 private template TypeOf(alias T) {
59     static if (__traits(compiles, typeof(T))) {
60         alias TypeOf = typeof(T);
61     } else {
62         alias TypeOf = T;
63     }
64 }
65 
66 template isTypesAttr(alias T) {
67     import unit_threaded.attrs;
68 
69     enum isTypesAttr = is(T) && is(T : Types!U, U...);
70 }
71 
72 /*
73  @Types is different from the other UDAs since it's a templated struct
74  None of the templates above work so we special case it here
75 */
76 
77 /// If a test has the @Types UDA
78 enum HasTypes(alias T) = GetTypes!T.length > 0;
79 
80 /// Returns the types in the @Types UDA associated to a test
81 template GetTypes(alias T) {
82     import std.meta : Filter, AliasSeq;
83     import std.traits : TemplateArgsOf;
84 
85     static if (!__traits(compiles, __traits(getAttributes, T))) {
86         alias GetTypes = AliasSeq!();
87     } else {
88         alias types = Filter!(isTypesAttr, __traits(getAttributes, T));
89         static if (types.length > 0)
90             alias GetTypes = TemplateArgsOf!(types[0]);
91         else
92             alias GetTypes = AliasSeq!();
93     }
94 }
95 
96 // copy of recent hasUDA from Phobos here because old
97 // compilers will fail otherwise
98 
99 enum hasUtUDA(alias symbol, alias attribute) = getUtUDAs!(symbol, attribute).length > 0;
100 
101 template getUtUDAs(alias symbol, alias attribute) {
102     import std.meta : Filter;
103     import std.traits : isInstanceOf;
104 
105     template isDesiredUDA(alias toCheck) {
106         static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) {
107             static if (__traits(compiles, toCheck == attribute))
108                 enum isDesiredUDA = toCheck == attribute;
109             else
110                 enum isDesiredUDA = false;
111         } else static if (is(typeof(toCheck))) {
112             static if (__traits(isTemplate, attribute))
113                 enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck));
114             else
115                 enum isDesiredUDA = is(typeof(toCheck) == attribute);
116         } else static if (__traits(isTemplate, attribute))
117             enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
118         else
119             enum isDesiredUDA = is(toCheck == attribute);
120     }
121 
122     alias getUtUDAs = Filter!(isDesiredUDA, __traits(getAttributes, symbol));
123 }